﻿using AutoMapper;
using Compass.PlanService.Domain;
using Compass.PlanService.Domain.Entities;
using Compass.PlanService.Infrastructure;
using Compass.Wasm.Shared;
using Compass.Wasm.Shared.Categories;
using Compass.Wasm.Shared.Plans;

namespace Compass.Wasm.Server.Services.Plans;
public interface IFinishedGoodsCountService : IBaseService<FinishedGoodsCountDto>
{
    Task<ApiResponse<IEnumerable<FinishedGoodsCountDto>>> GetAllWithFeeAsync(bool showAll);
}
public class FinishedGoodsCountService : IFinishedGoodsCountService
{
    #region ctor
    private readonly PlanDbContext _dbContext;
    private readonly IPlanRepository _repository;
    private readonly IMapper _mapper;
    public FinishedGoodsCountService(PlanDbContext dbContext, IPlanRepository repository, IMapper mapper)
    {
        _dbContext = dbContext;
        _repository = repository;
        _mapper = mapper;
    }
    #endregion

    #region 基本增删改查

    public async Task<ApiResponse<IEnumerable<FinishedGoodsCountDto>>> GetAllAsync()
    {
        try
        {
            var models = await _repository.GetFinishedGoodsCountsAsync();
            var orderModels = models.OrderBy(x => x.OdpNumber);
            var dtos = await _mapper.ProjectTo<FinishedGoodsCountDto>(orderModels).ToListAsync();
            dtos.ForEach(x => x.CanEdit = true);
            return new ApiResponse<IEnumerable<FinishedGoodsCountDto>> { Status = true, Result = dtos };
        }
        catch (Exception e)
        {
            return new ApiResponse<IEnumerable<FinishedGoodsCountDto>> { Status = false, Message = e.Message };
        }
    }

    public async Task<ApiResponse<FinishedGoodsCountDto>> GetSingleAsync(Guid id)
    {
        try
        {
            var model = await _repository.GetFinishedGoodsCountByIdAsync(id);
            if (model == null) return new ApiResponse<FinishedGoodsCountDto> { Status = false, Message = "查询数据失败" };
            var dto = _mapper.Map<FinishedGoodsCountDto>(model);
            return new ApiResponse<FinishedGoodsCountDto> { Status = true, Result = dto };
        }
        catch (Exception e)
        {
            return new ApiResponse<FinishedGoodsCountDto> { Status = false, Message = e.Message };
        }
    }

    public async Task<ApiResponse<FinishedGoodsCountDto>> AddAsync(FinishedGoodsCountDto dto)
    {
        try
        {
            var model = new FinishedGoodsCount(Guid.NewGuid(), dto.OdpNumber, dto.Name, dto.Sbu, dto.PalletQuantity, dto.FinishTime, dto.WarehousingTime, dto.ShippingTime, dto.OutsideTime, dto.OutsideArea, dto.Location);
            await _dbContext.FinishedGoodsCounts.AddAsync(model);
            dto.Id = model.Id;
            return new ApiResponse<FinishedGoodsCountDto> { Status = true, Result = dto };
        }
        catch (Exception e)
        {
            return new ApiResponse<FinishedGoodsCountDto> { Status = false, Message = e.Message };
        }
    }

    public async Task<ApiResponse<FinishedGoodsCountDto>> UpdateAsync(Guid id, FinishedGoodsCountDto dto)
    {
        try
        {
            var model = await _repository.GetFinishedGoodsCountByIdAsync(id);
            if (model == null) return new ApiResponse<FinishedGoodsCountDto> { Status = false, Message = "更新数据失败" };
            model.Update(dto);

            return new ApiResponse<FinishedGoodsCountDto> { Status = true, Result = dto };
        }
        catch (Exception e)
        {
            return new ApiResponse<FinishedGoodsCountDto> { Status = false, Message = e.Message };
        }
    }

    public async Task<ApiResponse<FinishedGoodsCountDto>> DeleteAsync(Guid id)
    {
        try
        {
            var model = await _repository.GetFinishedGoodsCountByIdAsync(id);
            if (model == null) return new ApiResponse<FinishedGoodsCountDto> { Status = false, Message = "删除数据失败" };
            model.SoftDelete();//软删除
            return new ApiResponse<FinishedGoodsCountDto> { Status = true };
        }
        catch (Exception e)
        {
            return new ApiResponse<FinishedGoodsCountDto> { Status = false, Message = e.Message };
        }
    }
    #endregion

    public async Task<ApiResponse<IEnumerable<FinishedGoodsCountDto>>> GetAllWithFeeAsync(bool showAll)
    {
        try
        {
            //从数据库查询到固定内容
            var dtos = (await GetAllAsync()).Result.Where(x=> showAll || x.ShippingTime==null).ToList();

            //再从计划查询到1.完工时间是今年以前入库但未发货的项目,2.完工时间是今年的入库了的
            //从是否需要从2024年开始统计
            var startYear = new DateTime(DateTime.Now.Year, 1, 1);
            var mainPlans = (await _repository.GetMainPlansAsync()).Where(
                x => (x.FinishTime<startYear && x.WarehousingTime!=null && x.ShippingTime==null) || (x.FinishTime>=startYear && x.WarehousingTime!=null)).OrderBy(x => x.Number);

            foreach (var mainPlan in mainPlans)
            {

                var packingList = await _repository.GetPackingListByIdAsync(mainPlan.Id);
                if (packingList == null) continue;//如果这个订单没有装箱清单，就跳过统计

                var pallets = await _repository.GetPalletsByMainPlanIdAsync(packingList.Id);

                if (!pallets.Any()) continue;//如果这个订单没有装箱清单，就跳过统计

                //统计未发货的项目，需要计算货物面积
                if (pallets.Any(x => x.ShippingTime == null))
                {
                    var unShipPallets = pallets.Where(x => x.ShippingTime == null).ToList();
                    var dtoNotShipping = new FinishedGoodsCountDto
                    {
                        OdpNumber = mainPlan.Number,
                        Name = mainPlan.Name,
                        Sbu = Sbu_e.FS,
                        PalletQuantity = unShipPallets.Count,
                        FinishTime = mainPlan.FinishTime,
                        WarehousingTime = mainPlan.WarehousingTime,
                        Location = unShipPallets.First().Location,
                        MainPlanId = mainPlan.Id,
                        Partially = unShipPallets.Count<pallets.Count(),
                        Area = unShipPallets.Sum(GetArea)
                    };
                    dtos.Add(dtoNotShipping);
                }
                //已发货的托盘，单独统计，showAll
                if (showAll && pallets.Any(x => x.ShippingTime != null))
                {
                    var quantity = pallets.Count(x => x.ShippingTime!=null);

                    var dto = new FinishedGoodsCountDto
                    {
                        OdpNumber = mainPlan.Number,
                        Name = mainPlan.Name,
                        Sbu = Sbu_e.FS,
                        PalletQuantity = quantity,
                        FinishTime = mainPlan.FinishTime,
                        WarehousingTime = mainPlan.WarehousingTime,
                        ShippingTime = mainPlan.ShippingTime,
                        Location = pallets.First(x => x.ShippingTime!=null).Location,
                        MainPlanId = mainPlan.Id,
                        Partially = quantity<pallets.Count()
                    };
                    dtos.Add(dto);
                }
            }

            return new ApiResponse<IEnumerable<FinishedGoodsCountDto>> { Status = true, Result = CalculateAgingAndFee(dtos) };
        }
        catch (Exception e)
        {
            return new ApiResponse<IEnumerable<FinishedGoodsCountDto>> { Status = false, Message = e.Message };
        }
    }

    private static double GetArea(Pallet pallet)
    {
        var length=string.IsNullOrEmpty(pallet.PalletLength)?0:Convert.ToDouble(pallet.PalletLength);
        var width = string.IsNullOrEmpty(pallet.PalletWidth) ? 0 : Convert.ToDouble(pallet.PalletWidth);
        return Math.Round(length * width / 1000000d);
    }

    private List<FinishedGoodsCountDto> CalculateAgingAndFee(List<FinishedGoodsCountDto> dtos)
    {
        foreach (var dto in dtos)
        {
            //计算库龄
            //库龄=IF(H163=0,DATEDIF(G163,TODAY(), "d")+1,H163-G163)
            dto.Aging = dto.ShippingTime == null ?
                DateTime.Now.Subtract(dto.FinishTime).Days + 1<0 ? 0 :
                    DateTime.Now.Subtract(dto.FinishTime).Days +1 :
                dto.ShippingTime.Value.Subtract(dto.FinishTime).Days<0 ? 0 :
                    dto.ShippingTime.Value.Subtract(dto.FinishTime).Days;
            //计算预估仓储费
            //From the figures I collected , the target is to invoice on the following basis :
            //50rmb/m²/day (when palettes czan be stacked , there are stacked but we don’t optimize stacking if the quality and the security of the products (and our people) is compromised.
            //+100 RMB fee for in/out 
            //To make it clear for everyone, if I take a project were we have X palettes representing 20m², and the customer requests pick up 25 days after the confirmed date :
            //We have 14 days free and 11 days to be charged
            //Then storage costs is 11 * 50 = 550 RMB  + 100 RMB = 650 RMB
            //Knowing sales , there is big chance that they try to avoid this service but we need to hold tight. I belive that they will ask for counterpart, and the obvious counter part if delivery reliability.
            //In standard Halton terms contract, Halton shall be entitled to liquidated  damage of delay for internal reason.
            //The contract stipulates that charge costs can be at the level of 0,5% of price of the delayed products per starting week to a maximum of 7,5 % of the delayed products.  

            //预估仓储费=IF((I163-14)*'Storage fee rules'!$F$20*'Storage fee rules'!$E$20*E163/'Storage fee rules'!$H$20+'Storage fee rules'!$G$20<0,0,(I163-14)*'Storage fee rules'!$F$20*'Storage fee rules'!$E$20*E163/'Storage fee rules'!$H$20+'Storage fee rules'!$G$20)
            //简化公式
            //'Storage fee rules'!$F$20=unit price(RMB/M^2)=50
            //'Storage fee rules'!$E$20=standard size=1.6
            //'Storage fee rules'!$H$20=stacked level=2
            //'Storage fee rules'!$G$20= in/out fee =100

            //预估仓储费=IF((dto.Aging-14)*UnitPrice*StandardSize*dto.PalletQuantity/StackedLevel+InOutFee<0,0,(dto.Aging-14)*UnitPrice*StandardSize*dto.PalletQuantity/StackedLevel+InOutFee)
            const double UnitPrice = 50d;
            const double StandardSize = 1.6;
            const double StackedLevel = 2;
            const double InOutFee = 100;
            var fee = (dto.Aging - 14) * UnitPrice * StandardSize * dto.PalletQuantity / StackedLevel + InOutFee;
            dto.StorageFee = fee < 0 ? 0 : fee;
        }
        return dtos;
    }
    
}